﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Civilisation_code.PCarte;
using Civilisation_code.PInterfaces_Fabrique_Unite;
using System.ComponentModel;

namespace Civilisation_code.PJoueur
{
    /// <summary>
    /// Classe Ville contenant toutes les informations d'une ville
    /// </summary>
    [Serializable]
    public class Ville
    {
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        /// <summary>
        /// Minerai disponible dans la ville
        /// </summary>
        public int _minerai { get; set; }

        /// <summary>
        /// Fruit disponible dans la ville
        /// </summary>
        public int _nourriture { get; set; }


        /// <summary>
        /// Population de la ville
        /// </summary>
        public int _population { get; set; }

        /// <summary>
        /// Unité à produire dans la ville
        /// </summary>
        private string _uniteAProduire;
        public string UniteAProduire
        {
            get 
            { 
                return _uniteAProduire; 
            }
            set
            {
                _uniteAProduire = value;
                _minerai = 0;
                OnPropertyChanged("changement_unite_a_produire");

            }
        }

        /// <summary>
        /// Joueur possédant la ville
        /// </summary>
        private Joueur _joueur;
        public Joueur Joueur
        {
            get
            {
                return _joueur;
            }
            set
            {

                this._joueur = value;
                OnPropertyChanged("prise");

            }

        }

        /// <summary>
        /// Case où est située la ville
        /// </summary>
        private Case _case;
        public Case Case
        {
            get{
                return _case;
            }
            set
            {
                _case = value;
            }
        }

        /// <summary>
        /// Liste des cases annexées par la ville
        /// </summary>
        public List<Case> _cases_annexes { get; set; }

        /// <summary>
        /// Coût en fruit du dernier agrandissement de la population
        /// </summary>
        private int _cout_evolution=10; 

        /// <summary>
        /// Constructeur
        /// </summary>
        /// <param name="c">Case où est située la ville</param>
        /// <param name="j">Joueur possédant la ville</param>
        public Ville(Case c, Joueur j)
        {
            //Initialisation des attributs
            _population = 1;
            _cases_annexes = new List<Case>();
            _case = c;
            _joueur = j;
            _joueur._villes.Add(this);
            c.Ville = this;
            _uniteAProduire = "Etudiant";
            

        }

        /// <summary>
        /// Liste des cases que la ville a une vue dessus
        /// </summary>
        /// <returns></returns>
        public List<Case> vueSur()
        {
            return _case.vueSur(3);
        }


        /// <summary>
        /// Liste des cases encore disponibles pour annexer
        /// </summary>
        /// <returns>Liste des cases encore disponibles pour annexer</returns>
        public List<Case> getCasesDisponibles()
        {
            List<Case> liste = new List<Case>();
            for(int i=_case._x-3; i<=_case._x+3;i++)
                for (int j = _case._y - 3; j <= _case._y + 3; j++)
                {
                    Case ca = PPartie.Partie.INSTANCE._carte.getCase(i, j);
                    if (ca != null && ca.Ville == null && ca.Ville_annexee == null && ca.calculerDistance(_case)<=3)
                    {
                        liste.Add(ca);
                    }
                }

            liste.Sort(ComparerCases);
            return liste;
        }

        /// <summary>
        /// Méthode qui compare 2 cases
        /// </summary>
        /// <param name="a">case 1</param>
        /// <param name="b">case 2</param>
        /// <returns>la comparaison</returns>
        private int ComparerCases(Case a, Case b)
        {
            //On trie les cases par distance
            double distA = Math.Sqrt((_case._x - a._x) * (_case._x - a._x) + (_case._y - a._y) * (_case._y - a._y));
            double distB = Math.Sqrt((_case._x - b._x) * (_case._x - b._x) + (_case._y - b._y) * (_case._y - b._y));
            return distA.CompareTo(distB);
        }

        /// <summary>
        /// Méthode dont le but est de faire le traitement des tâches à affectuer au début d'un tour
        /// </summary>
        public void nouveauTour()
        {
            augmenterRessources();
            agrandirVillage();
            produireUnite();
        }

        /// <summary>
        /// Méthode d'agrandissement de la ville
        /// </summary>
        private void agrandirVillage()
        {
            if (_nourriture >= _cout_evolution)
            {
                _population += 1;
                _nourriture = _nourriture - _cout_evolution;
                _cout_evolution = _cout_evolution + _cout_evolution / 2;

                //On vient d'agrandir la ville, on annexe donc une case
                List<Case> casesDispo = getCasesDisponibles();
                if (casesDispo.Count > 0)
                {
                    Case annexee = casesDispo[0];
                    annexee.Ville_annexee = this;
                    _cases_annexes.Add(annexee);
                }
            }
        }

        /// <summary>
        /// Augmentation des ressources au sein du village
        /// </summary>
        private void augmenterRessources()
        {
            _minerai += productionMinerais();
            if(!_uniteAProduire.Equals("Enseignant"))
                _nourriture += productionFruit();
        }


        /// <summary>
        /// Indique si il y a assez de ressources pour produire une unité d'un type donné en paramètre
        /// </summary>
        /// <param name="type">Type de l'unité (Directeur, Enseignant, Etudiant)</param>
        /// <returns></returns>
        private bool possibiliteCreationUnite(string type)
        {
            Console.WriteLine("Demande de production de {0} de cout {1} avec minerais = {2}", type, coutCreationUnite(type),_minerai);

            //Si le joueur a déjà un directeur et qu'il souhaite en produire un ...
            if(type.Equals("Directeur") && _joueur.possedeDirecteur())
                return false;
            return _minerai >= coutCreationUnite(type);
        }

        /// <summary>
        /// Coût de la création d'une unité
        /// </summary>
        /// <param name="type">Type de l'unité (Directeur, Enseignant ou Etudiant)</param>
        /// <returns></returns>
        private int coutCreationUnite(string type)
        {
            switch (_uniteAProduire)
            {
                case "Etudiant":
                    return Etudiant.cout;
                case "Enseignant":
                    return Enseignant.cout;
                case "Directeur":
                    return Directeur.cout;
                default :
                    return 0;
            }
        }

        /// <summary>
        /// Production de l'unité en attente
        /// </summary>
        private void produireUnite()
        {
            if (possibiliteCreationUnite(_uniteAProduire))
            {
                switch (_uniteAProduire)
                {
                    case "Etudiant":
                        _joueur._fabrique.creerEtudiant(_case, _joueur);
                        _minerai -= Etudiant.cout;
                        break;
                    case "Enseignant":
                        _joueur._fabrique.creerEnseignant(_case, _joueur);
                        _minerai -= Enseignant.cout;
                        break;
                    case "Directeur":
                        _joueur._fabrique.creerDirecteur(_case, _joueur);
                        _minerai -= Directeur.cout;
                        _uniteAProduire = "Etudiant";
                        break;
                }
            }
        }

        /// <summary>
        /// Donne la production total en minerais du village
        /// </summary>
        /// <returns>la production en minerais</returns>
        public int productionMinerais(){
            
            int tmp = 0;
            //Ajout des ressources produites des cases annexées
            foreach (Case c in _cases_annexes)
            {
                tmp += c._ressource.minerais;
            }

            //Ajout des ressources produites par la case de la ville
            return  tmp + _case._ressource.minerais;
        }

        /// <summary>
        /// Donne la production total en fruits du village
        /// </summary>
        /// <returns>la production en fruits</returns>
        public int productionFruit()
        {
            int tmp = 0;
            //Ajout des ressources produites des cases annexées
            foreach (Case c in _cases_annexes)
            {
                tmp += c._ressource.nourriture;
            }

            //Ajout des ressources produites par la case de la ville
            return tmp + _case._ressource.nourriture;
        }
    }
}
